#include "Optimization.h"
#include "Exception.h"
#include "TransformInvariant.h"

namespace Compiler
{
	namespace Intermediate
	{
		class PeepHoleOptimizer : public IntraProcOptimizer
		{
		private:
			static bool IsPowerOfTwo(int val)
			{
				return val > 1 && (val & (val-1)) == 0;
			}
			static int Log2(int val)
			{
				int x=0;  
				while(val>1)  
				{  
					val>>=1;  
					x++;  
				}  
				return x;  
			}
		public:
			virtual ProgramOptimizationResult Optimize(RefPtr<ControlFlowGraph> program) override
			{
				ProgramOptimizationResult result;
				result.Program = program;
				result.Changed = false;
				for (auto & node : program->Nodes)
				{
					for (auto instrNode = node->Code.begin(); instrNode != node->Code.end(); ++instrNode)
					{
						auto & instr = instrNode.Current->Value;
						if (instr.Func == Operation::Mod && instr.Operands[1].IsIntegral() &&
							IsPowerOfTwo(instr.Operands[1].IntValue))
						{
							instr.Func = Operation::BitAnd;
							instr.Operands[1].IntValue--;
							if (instr.Operands[1].GetDataType() == DataType::Int) // signed
								instr.Operands[1].IntValue |= (int)0x80000000;
						}
						if (instr.Func == Operation::Div && instr.Operands[1].IsIntegral() &&
							IsPowerOfTwo(instr.Operands[1].IntValue))
						{
							instr.Func = Operation::Rsh;
							instr.Operands[1].IntValue = Log2(instr.Operands[1].IntValue);
						}
						if (instr.Func == Operation::Mul && instr.Operands[1].IsIntegral() &&
							IsPowerOfTwo(instr.Operands[1].IntValue))
						{
							instr.Func = Operation::Lsh;
							instr.Operands[1].IntValue = Log2(instr.Operands[1].IntValue);
						}
					}
				}
				return result;
			}
		};

		IntraProcOptimizer * CreatePeepHoleOptimizer()
		{
			return new PeepHoleOptimizer();
		}
	}
}
